﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DcsGauges.Shared.DataStructures;
using DcsGauges.Shared.Exceptions;
using System.Diagnostics.Contracts;
using DcsGauges.Gui.Gauges.Interfaces;

namespace DcsGauges.Gui.Gauges.ValueConverters
{
    public class IntervalStateConverter<T> : IGaugeValueConverter<float, T>
    {
        #region Field & Properties

        private List<Tuple<Interval, T>>            intervalToStateMapping                  = null;
        private float                               intervalMin                             = float.MinValue;
        private float                               intervalMax                             = float.MaxValue;

        #endregion


        #region Constructors

        public IntervalStateConverter(List<Tuple<Interval, T>> intervalToState)
        {
            if(intervalToState == null)         throw new DcsArgumentNullException(string.Format(Localization.ExcArgumentWasNull1A, "intervalToState"));
            if(intervalToState.Count <= 0)      throw new DcsArgumentException(string.Format(Localization.ExcAtLeastOneElementRequired1A, "intervalToState"));
            Contract.EndContractBlock();

            this.intervalToStateMapping         = intervalToState;
            this.CheckIntervals();

            this.intervalMin                    = this.intervalToStateMapping.First().Item1.Begin;
            this.intervalMax                    = this.intervalToStateMapping.Last().Item1.End;

            Contract.Ensures(this.intervalToStateMapping != null);
            Contract.Ensures(this.intervalToStateMapping.Count > 0);
        }

        #endregion


        #region Interface Implementation

        public T Convert(float value)
        {

            if(value < this.intervalMin || value > this.intervalMax) throw new DcsArgumentException(string.Format(Localization.ExcValueOutOfRange3A, value, this.intervalMin, this.intervalMax));
            Contract.EndContractBlock();

            for(int i = 0; i < this.intervalToStateMapping.Count; i++)
            {
                if(value >= this.intervalToStateMapping[i].Item1.Begin && value <= this.intervalToStateMapping[i].Item1.End)
                {
                    return this.intervalToStateMapping[i].Item2;
                }
            }

            throw new InvalidOperationException("The class [IntervalStateConverter] doesn't work as expected");
        }

        public float ConvertBack(T value)
        {
            Contract.Requires(this.intervalToStateMapping.Count > 0);
            Contract.Requires(this.intervalToStateMapping.FirstOrDefault((item => item.Item2.Equals(value))) != null);
            Contract.Ensures(Contract.Result<float>() > this.intervalMin);
            Contract.Ensures(Contract.Result<float>() < this.intervalMax);

            var found           = this.intervalToStateMapping.Where((item) => item.Item2.Equals(value)).First();
            float converted     = (found.Item1.Begin + found.Item1.End) * 0.5f;
            return converted;
        }

        #endregion


        #region Contract Invariants

        [ContractInvariantMethod]
        private void ContractInvariant()
        {
            for (int i = 0; i < intervalToStateMapping.Count - 1; i++)
            {
                Contract.Requires(this.intervalToStateMapping[i].Item1.End <= this.intervalToStateMapping[i + 1].Item1.Begin);
            }
        }

        #endregion


        #region Helpers & Internals

        private void CheckIntervals()
        {
            for (int i = 0; i < intervalToStateMapping.Count - 1; i++)
            {
                if (this.intervalToStateMapping[i].Item1.End > this.intervalToStateMapping[i + 1].Item1.Begin)
                {
                    throw new DcsArgumentException(string.Format(Localization.ExcIntervalsOverlap2A, i, i + 1));
                }
            }
        }

        #endregion
    }
}
